#include "com_voe_VoERTP_RTCP.h"
#include "jni_api.h"
#include "OutputParam.h"
#include "common_types.h"
#include "voe_rtp_rtcp.h"
#include "VoERTPObserverImpl.h"
#include "VoERTCPObserverImpl.h"

static VoERTPObserverImpl _VoERTPObserver;
static VoERTCPObserverImpl _VoERTCPObserver;

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetInterface
 * Signature: (J)J
 */
JNIEXPORT jlong JNICALL Java_com_voe_VoERTP_1RTCP_GetInterface
	(JNIEnv* env, jclass cls, jlong p_voiceEngine)
{
	jlong result = 0;

	do 
	{
		webrtc::VoERTP_RTCP* p = 0;

		if(!p_voiceEngine)
			break;

		p = webrtc::VoERTP_RTCP::GetInterface((webrtc::VoiceEngine*)p_voiceEngine);

		result = (jlong)p;

	} while (false);

	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    Release
 * Signature: (J)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_Release
	(JNIEnv* env, jclass cls, jlong p)
{
	jint result = 0;
	if(p)
	{
		webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
		result = pVoERTP_RTCP->Release();
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    RegisterRTPObserver
 * Signature: (JILcom/voe/callback/VoERTPObserver;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_RegisterRTPObserver
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	if(_VoERTPObserver.set(env, obj))
	{
		return pVoERTP_RTCP->RegisterRTPObserver(channel, _VoERTPObserver);
	}
	return JCB_Err_Set;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    DeRegisterRTPObserver
 * Signature: (JI)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_DeRegisterRTPObserver
	(JNIEnv* env, jclass cls, jlong p, jint channel)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	jint result = pVoERTP_RTCP->DeRegisterRTPObserver(channel);
	if(result == 0)
		_VoERTPObserver.unset(env);
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    RegisterRTCPObserver
 * Signature: (JILcom/voe/callback/VoERTCPObserver;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_RegisterRTCPObserver
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	if(_VoERTPObserver.set(env, obj))
	{
		return pVoERTP_RTCP->RegisterRTCPObserver(channel, _VoERTCPObserver);
	}
	return JCB_Err_Set;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    DeRegisterRTCPObserver
 * Signature: (JI)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_DeRegisterRTCPObserver
	(JNIEnv* env, jclass cls, jlong p, jint channel)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	jint result = pVoERTP_RTCP->DeRegisterRTCPObserver(channel);
	if(result == 0)
		_VoERTPObserver.unset(env);
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    SetLocalSSRC
 * Signature: (JII)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_SetLocalSSRC
	(JNIEnv* env, jclass cls, jlong p, jint channel, jint ssrc)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->SetLocalSSRC(channel, ssrc);
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetLocalSSRC
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetLocalSSRC
  (JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	unsigned int ssrc = 0;
	jint result = pVoERTP_RTCP->GetLocalSSRC(channel, ssrc);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add((int)ssrc).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRemoteSSRC
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRemoteSSRC
  (JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	unsigned int ssrc = 0;
	jint result = pVoERTP_RTCP->GetRemoteSSRC(channel, ssrc);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add((int)ssrc).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    SetRTPAudioLevelIndicationStatus
 * Signature: (JIZC)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_SetRTPAudioLevelIndicationStatus
	(JNIEnv* env, jclass cls, jlong p, jint channel, jboolean enable, jchar ID)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->SetRTPAudioLevelIndicationStatus(channel, enable!=0, (unsigned char)ID);
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRTPAudioLevelIndicationStatus
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRTPAudioLevelIndicationStatus
  (JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	bool enabled;
	unsigned char ID;
	jint result = pVoERTP_RTCP->GetRTPAudioLevelIndicationStatus(channel, enabled, ID);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add(enabled).add((int)ID).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRemoteCSRCs
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRemoteCSRCs
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	unsigned int arrCSRC[15] = {0};
	jint result = pVoERTP_RTCP->GetRemoteCSRCs(channel, arrCSRC);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add((int*)arrCSRC, sizeof(arrCSRC)/sizeof(unsigned int)).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    SetRTCPStatus
 * Signature: (JIZ)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_SetRTCPStatus
	(JNIEnv* env, jclass cls, jlong p, jint channel, jboolean enable)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->SetRTCPStatus(channel, enable!=0);
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRTCPStatus
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRTCPStatus
  (JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	bool enabled;
	jint result = pVoERTP_RTCP->GetRTCPStatus(channel, enabled);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add(enabled).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    SetRTCP_CNAME
 * Signature: (JILjava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_SetRTCP_1CNAME
	(JNIEnv* env, jclass cls, jlong p, jint channel, jstring cName)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->SetRTCP_CNAME(channel, JniHelper::j2c_string(env, cName).c_str());
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRTCP_CNAME
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRTCP_1CNAME
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	char cName[256] = {0};
	jint result = pVoERTP_RTCP->GetRTCP_CNAME(channel, cName);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add(std::string(cName)).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRemoteRTCP_CNAME
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRemoteRTCP_1CNAME
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	char cName[256] = {0};
	jint result = pVoERTP_RTCP->GetRemoteRTCP_CNAME(channel, cName);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add(std::string(cName)).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRemoteRTCPData
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRemoteRTCPData
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	unsigned int NTPHigh, NTPLow, timestamp, playoutTimestamp, jitter;
	unsigned short fractionLost;
	jint result = pVoERTP_RTCP->GetRemoteRTCPData(channel, NTPHigh, NTPLow, timestamp, playoutTimestamp, &jitter, &fractionLost);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job
			.add((int)NTPHigh)
			.add((int)NTPLow)
			.add((int)timestamp)
			.add((int)playoutTimestamp)
			.add((int)jitter)
			.add((int)fractionLost)
			.getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRTPStatistics
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRTPStatistics
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	unsigned int averageJitterMs, maxJitterMs, discardedPackets;
	jint result = pVoERTP_RTCP->GetRTPStatistics(channel, averageJitterMs, maxJitterMs, discardedPackets);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job
			.add((int)averageJitterMs)
			.add((int)maxJitterMs)
			.add((int)discardedPackets)
			.getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRTCPStatistics
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRTCPStatistics
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	webrtc::CallStatistics stats = {0};
	jint result = pVoERTP_RTCP->GetRTCPStatistics(channel, stats);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job
			.add((int)stats.fractionLost)
			.add((int)stats.cumulativeLost)
			.add((int)stats.extendedMax)
			.add((int)stats.jitterSamples)
			.add(stats.rttMs)
			.add(stats.bytesSent)
			.add(stats.packetsSent)
			.add(stats.bytesReceived)
			.add(stats.packetsReceived)
			.getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRemoteRTCPSenderInfo
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRemoteRTCPSenderInfo
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	webrtc::SenderInfo sender_info = {0};
	jint result = pVoERTP_RTCP->GetRemoteRTCPSenderInfo(channel, &sender_info);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job
			.add((int)sender_info.NTP_timestamp_high)
			.add((int)sender_info.NTP_timestamp_low)
			.add((int)sender_info.RTP_timestamp)
			.add((int)sender_info.sender_packet_count)
			.add((int)sender_info.sender_octet_count)
			.getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetRemoteRTCPReportBlocks
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetRemoteRTCPReportBlocks
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	std::vector<webrtc::ReportBlock> receive_blocks;
	jint result = pVoERTP_RTCP->GetRemoteRTCPReportBlocks(channel, &receive_blocks);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		for (int i=0;i<(int)receive_blocks.size();i++)
		{
			JniOutputBuilder job_item(env);
			OutputParam param_item(env);
			webrtc::ReportBlock& rb = receive_blocks.at(i);
			param_item.setArray(job_item.add((int)rb.sender_SSRC)
				.add((int)rb.source_SSRC)
				.add((int)rb.fraction_lost)
				.add((int)rb.cumulative_num_packets_lost)
				.add((int)rb.extended_highest_sequence_number)
				.add((int)rb.interarrival_jitter)
				.add((int)rb.last_SR_timestamp)
				.add((int)rb.delay_since_last_SR).getOutput());
			job.add(param_item.getOutput());
		}
		param.setArray(job.getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    SendApplicationDefinedRTCPPacket
 * Signature: (JICI[B)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_SendApplicationDefinedRTCPPacket
	(JNIEnv* env, jclass cls, jlong p, jint channel, jchar subType, jint name, jbyteArray array)
{
	int dataLengthInBytes = (env)->GetArrayLength(array);
	char* data = new char[dataLengthInBytes];
	JniHelper::j2c_bytes(env, array, (unsigned char*)data, dataLengthInBytes);
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	webrtc::SenderInfo sender_info = {0};
	jint result = pVoERTP_RTCP->SendApplicationDefinedRTCPPacket(channel, (unsigned char)subType, name, data, dataLengthInBytes);
	delete [] data;
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    SetFECStatus
 * Signature: (JIZI)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_SetFECStatus
	(JNIEnv* env, jclass cls, jlong p, jint channel, jboolean enable, jint redPayloadtype)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->SetFECStatus(channel, enable!=0, redPayloadtype);
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetFECStatus
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetFECStatus
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	bool enabled;
	int redPayloadtype;
	jint result = pVoERTP_RTCP->GetFECStatus(channel, enabled, redPayloadtype);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add(enabled).add(redPayloadtype).getOutput());
	}
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    StartRTPDump
 * Signature: (JILjava/lang/String;I)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_StartRTPDump
	(JNIEnv* env, jclass cls, jlong p, jint channel, jstring fileNameUTF8, jint direction)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->StartRTPDump(channel, JniHelper::j2c_string(env, fileNameUTF8).c_str(),
		(webrtc::RTPDirections)direction);
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    StopRTPDump
 * Signature: (JII)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_StopRTPDump
	(JNIEnv* env, jclass cls, jlong p, jint channel, jint direction)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->StopRTPDump(channel, (webrtc::RTPDirections)direction);
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    RTPDumpIsActive
 * Signature: (JII)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_RTPDumpIsActive
	(JNIEnv* env, jclass cls, jlong p, jint channel, jint direction)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	return pVoERTP_RTCP->RTPDumpIsActive(channel, (webrtc::RTPDirections)direction);
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    InsertExtraRTPPacket
 * Signature: (JICZ[B)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_InsertExtraRTPPacket
	(JNIEnv* env, jclass cls, jlong p, jint channel, jchar payloadType, jboolean markerBit, jbyteArray array)
{
	unsigned int payloadSize = (env)->GetArrayLength(array);
	char* payloadData = new char[payloadSize];
	JniHelper::j2c_bytes(env, array, (unsigned char*)payloadData, (int)payloadSize);
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	jint result = pVoERTP_RTCP->InsertExtraRTPPacket(channel, (unsigned char)payloadType, markerBit!=0, payloadData, payloadSize);
	delete [] payloadData;
	return result;
}

/*
 * Class:     com_voe_VoERTP_RTCP
 * Method:    GetLastRemoteTimeStamp
 * Signature: (JILcom/voe/common/OutputParam;)I
 */
JNIEXPORT jint JNICALL Java_com_voe_VoERTP_1RTCP_GetLastRemoteTimeStamp
	(JNIEnv* env, jclass cls, jlong p, jint channel, jobject obj)
{
	webrtc::VoERTP_RTCP* pVoERTP_RTCP = (webrtc::VoERTP_RTCP*)p;
	uint32_t lastRemoteTimeStamp;
	jint result = pVoERTP_RTCP->GetLastRemoteTimeStamp(channel, &lastRemoteTimeStamp);
	if(result == 0)
	{
		JniOutputBuilder job(env);
		OutputParam param(env, obj);
		param.setArray(job.add((int)lastRemoteTimeStamp).getOutput());
	}
	return result;
}
